package com.xjrsoft.module.organization.task;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.util.TypeUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.config.CommonPropertiesConfig;
import com.xjrsoft.config.RequestInfoConfig;
import com.xjrsoft.config.SyncOrganizationConfig;
import com.xjrsoft.module.organization.entity.*;
import com.xjrsoft.module.organization.service.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Component
@AllArgsConstructor
@EnableScheduling
public class SyncOrganizationTask implements SchedulingConfigurer {

    private final SyncOrganizationConfig config;

    private final IDepartmentService departmentService;

    private final IUserDeptRelationService userDeptRelationService;

    private final IPostService postService;

    private final IUserPostRelationService userPostRelationService;

    private final IUserService userService;

    private final CommonPropertiesConfig propertiesConfig;

    private final RedisUtil redisUtil;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        if (!BooleanUtils.isTrue(config.getEnabled())) {
            return;
        }
        long interval = config.getInterval() * 3600 * 1000;
        taskRegistrar.addFixedRateTask(() -> {
            try {
                log.info("---------------------开始同步部门---------------------");
                syncDepartments();
                log.info("---------------------部门同步结束---------------------");

                log.info("---------------------开始同步岗位---------------------");
                syncPosts();
                log.info("---------------------岗位同步结束---------------------");

                log.info("---------------------开始同步人员---------------------");
                syncUsers();
                log.info("---------------------人员同步结束---------------------");
                CompletableFuture.runAsync(() -> {
                    List<Department> depList = departmentService.list();
                    redisUtil.set(GlobalConstant.DEP_CACHE_KEY, depList);
                    List<UserDeptRelation> userDeptRelationList = userDeptRelationService.list();
                    redisUtil.set(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, userDeptRelationList);

                    List<Post> postList = postService.list();
                    redisUtil.set(GlobalConstant.POST_CACHE_KEY, postList);
                    List<UserPostRelation> userPostRelationList = userPostRelationService.list();
                    redisUtil.set(GlobalConstant.USER_POST_RELATION_CACHE_KEY, userPostRelationList);

                    List<User> userList = userService.list();
                    redisUtil.set(GlobalConstant.USER_CACHE_KEY, userList);
                });
            } catch (Throwable t) {
                log.error("同步组织架构数据出错！ ", t);
            }
        }, interval);
    }

    private void syncDepartments() {
        RequestInfoConfig requestInfo = config.getDepartment();
        JSONArray responseData = doRequest(requestInfo);
        List<Department> departmentList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(responseData)) {
            Map<String, String> entityKeyMap = requestInfo.getEntityKeyMap();
            for (Object obj : responseData) {
                JSONObject data = (JSONObject) obj;
                Department department = new Department();
                entityKeyMap.forEach((key, value) -> {
                    BeanUtil.setFieldValue(department, key, data.get(value));
                });
                if (department.getParentId() == null) {
                    department.setParentId(0L);
                }
                departmentList.add(department);
            }
        }
        departmentService.saveOrUpdateBatch(departmentList);
    }

    private void syncPosts() {
        RequestInfoConfig requestInfo = config.getPost();
        JSONArray responseData = doRequest(requestInfo);
        List<Post> postList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(responseData)) {
            Map<String, String> entityKeyMap = requestInfo.getEntityKeyMap();
            for (Object obj : responseData) {
                JSONObject data = (JSONObject) obj;
                Post post = new Post();
                entityKeyMap.forEach((key, value) -> {
                    BeanUtil.setFieldValue(post, key, data.get(value));
                });
                if (post.getParentId() == null) {
                    post.setParentId(0L);
                }
                postList.add(post);
            }
        }
        postService.saveOrUpdateBatch(postList);
    }

    private void syncUsers() {
        RequestInfoConfig requestInfo = config.getUser();
        JSONArray responseData = doRequest(requestInfo);
        if (CollectionUtils.isNotEmpty(responseData)) {
            List<User> userList = new ArrayList<>();
            List<UserDeptRelation> userDeptRelationList = new ArrayList<>();
            List<UserPostRelation> userPostRelationList = new ArrayList<>();
            Map<String, String> entityKeyMap = requestInfo.getEntityKeyMap();
            for (Object obj : responseData) {
                JSONObject data = (JSONObject) obj;
                User user = new User();
                Long deptId = null;
                Long postId = null;
                for (Map.Entry<String, String> entry : entityKeyMap.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    Object fieldValue = data.get(value);
                    if (fieldValue == null) {
                        continue;
                    }
                    if (StrUtil.equalsIgnoreCase(key, "deptId")) {
                        deptId = TypeUtils.castToLong(fieldValue);
                    } else if (StrUtil.equalsIgnoreCase(key, "postId")) {
                        postId = TypeUtils.castToLong(fieldValue);
                    } else {
                        BeanUtil.setFieldValue(user, key, fieldValue);
                    }
                }
                if (deptId != null) {
                    UserDeptRelation userDeptRelation = new UserDeptRelation();
                    userDeptRelation.setUserId(user.getId());
                    userDeptRelation.setDeptId(deptId);
                    userDeptRelationList.add(userDeptRelation);
                }
                if (postId != null) {
                    UserPostRelation userPostRelation = new UserPostRelation();
                    userPostRelation.setUserId(user.getId());
                    userPostRelation.setPostId(postId);
                    userPostRelationList.add(userPostRelation);
                }
                // 设置账号，默认密码
                user.setUserName(user.getCode());
                user.setPassword(SaSecureUtil.md5BySalt(propertiesConfig.getDefaultPassword(), GlobalConstant.SECRET_KEY));
                userList.add(user);
            }

            userService.saveOrUpdateBatch(userList);
            for (UserDeptRelation userDeptRelation : userDeptRelationList) {
                userDeptRelationService.saveOrUpdate(userDeptRelation, Wrappers.<UserDeptRelation>update().lambda()
                        .eq(UserDeptRelation::getDeptId, userDeptRelation.getDeptId())
                        .eq(UserDeptRelation::getUserId, userDeptRelation.getUserId()));
            }

            for (UserPostRelation userPostRelation : userPostRelationList) {
                userPostRelationService.saveOrUpdate(userPostRelation, Wrappers.<UserPostRelation>update().lambda()
                        .eq(UserPostRelation::getPostId, userPostRelation.getPostId())
                        .eq(UserPostRelation::getUserId, userPostRelation.getUserId()));
            }
        }
    }

    private String getToken() {
        RequestInfoConfig requestInfo = config.getToken();
        String result = StringUtils.EMPTY;
        if (StrUtil.equalsIgnoreCase(requestInfo.getMethod(), "get")) {
            result = HttpUtil.get(requestInfo.getUrl(), requestInfo.getParams());
        } else if (StrUtil.equalsIgnoreCase(requestInfo.getMethod(), "post")) {
            result = HttpUtil.post(requestInfo.getUrl(), requestInfo.getParams());
        }
        if (StrUtil.isBlank(result)) {
            return null;
        }
        JSONObject responseJson = JSONObject.parseObject(result);
        return responseJson.getString(requestInfo.getResponseDataKey());
    }

    private JSONArray doRequest(RequestInfoConfig requestInfo) {
        String url = requestInfo.getUrl();
        Map<String, Object> params = new HashMap<>();
        if (CollectionUtils.isNotEmpty(requestInfo.getParams())) {
            params.putAll(requestInfo.getParams());
        }

        // 构建请求
        HttpRequest request = HttpRequest.of(url);
        // 设置token
        if (config.getToken() != null) {
            String token = getToken();
            String tokenParamType = config.getTokenParamType();
            if (StrUtil.equalsIgnoreCase(tokenParamType, "head")) {
                request.header(config.getTokenParamKey(), token);
            } else if (StrUtil.equalsIgnoreCase(tokenParamType, "query")) {
                request.setUrl(UrlBuilder.of(url).addQuery(config.getTokenParamKey(), token));
            }
        }
        JSONArray resultList = new JSONArray();
        int page = 1;
        Long total = null;
        boolean isPage = BooleanUtils.isTrue(requestInfo.getIsPage());
        while (page < 1000) {
            if (isPage) {
                params.put(requestInfo.getPageParamKey(), Integer.toString(page));
                params.put(requestInfo.getPageSizeParamKey(), 100);
            }
            // 请求方式及参数设置
            if (StrUtil.equalsIgnoreCase(requestInfo.getMethod(), "get")) {
                request.setMethod(Method.GET).form(params);
            } else if (StrUtil.equalsIgnoreCase(requestInfo.getMethod(), "post")) {
                request.setMethod(Method.POST).body(JSONObject.toJSONString(params));
            }

            // 发送请求
            String result = request.execute().body();
            log.info("请求返回数据：" + result);

            // 返回数据处理
            if (StrUtil.isBlank(result)) {
                break;
            }
            String responseDataKey = requestInfo.getResponseDataKey();
            if (StrUtil.isBlank(responseDataKey)) {
                resultList.addAll(JSON.parseArray(result));
            }
            JSONObject responseJson = JSONObject.parseObject(result);
            String[] keys = StringUtils.split(responseDataKey, StringPool.DOT);
            JSONObject tempJson = responseJson;
            for (int i = 0; i < keys.length; i++) {
                if (i == keys.length - 1) {
                    resultList.addAll(tempJson.getJSONArray(keys[i]));
                } else {
                    tempJson = tempJson.getJSONObject(keys[i]);
                }
            }

            if (isPage) {
                // 分页接口处理
                if (total == null) {
                    String totalPageField = requestInfo.getTotalPageField();
                    if (StrUtil.isBlank(totalPageField)) {
                        break;
                    }
                    tempJson = responseJson;
                    String[] totalKeys = StringUtils.split(totalPageField, StringPool.DOT);
                    for (int i = 0; i < totalKeys.length; i++) {
                        if (i == totalKeys.length - 1) {
                            total = tempJson.getLong(totalKeys[i]);
                        } else {
                            tempJson = tempJson.getJSONObject(totalKeys[i]);
                        }
                    }
                }
                if (total == null || resultList.size() == total) {
                    break;
                }
                page++;
            } else {
                break;
            }
        }

        return resultList;
    }

}
